目录
  1. 1. makefile 语法详解
    1. 1.1. 编译流程详解
    2. 1.2. 静态库与动态库原理
      1. 1.2.1. 静态库
      2. 1.2.2. 动态库
    3. 1.3. makefile 走读与语法基础
      1. 1.3.1. 什么是 makefile
      2. 1.3.2. makefile 里有什么
      3. 1.3.3. makefile 规则
      4. 1.3.4. makefile 是如何工作的
      5. 1.3.5. makefile 变量使用
      6. 1.3.6. 引用其他的 Makefile
    4. 1.4. Android.mk 基础
  2. 2. Cmake 详解
  3. 3. shell 语法详解
高级Android-【C/C++理论实战技术】编译原理与语法详解

makefile 语法详解

编译流程详解

编译就是将高级语言编写的程序转化为二进制代码可执行性目标程序的过程。

编译分为四大过程:

  • 预处理(PreProcessing)

    • 完成宏替换、文件引入,以及去除空行、注释等,为下一步的编译做准备。

    • 也就是对各种预处理命令进行处理,包括头文件的包含、宏定义的扩展、条件编译的选择等等。

# test.c 文件内容
#include <stdio.h>

int main(){
printf("hello world!\n");
return 0;
}

--------------------------------------
$ gcc -E test.c -o tesi.i

选项 -E 让 gcc 在预处理结束后停止编译,“test.i” 文件为预处理后输出的文件。

选项 -o:指定输出文件
  • 编译(Compilation)

    • 将预处理后的代码编译成汇编代码。在这个阶段中,首先要检查代码的规范性、是否有语法错误等等,以确定代码实际要做的工作,再检查无误后,再把代码翻译成汇编语言。

    • 编译程序执行时,先分析,后综合。分析,就是指词法分析、语法分析、语义分析和中间代码生成。综合,就是指代码优化和代码生成。

    • 大多数的编译程序直接产生机器语言的目标代码,形成可执行的目标文件,也有的是先产生汇编语言一级的符号代码文件,再调用汇编程序进行翻译和加工处理,最后产生可执行的机器语言目标文件。

编译流程预处理截图1.png

# 上面是预处理后 test.i 文件部分内容
--------------------------------------
$ gcc -S test.i -o test.s

选项 -S 让 gcc 在编译结束后停止编译过程,“test.s” 文件为编译后生成的汇编代码。
  • 汇编(Assemble)

    • 汇编就是把编译阶段生成的 “.s” 文件转成二进制目标代码,也就是机器代码(01序列)。

编译流程汇编截图1.png

# 上面是编译后生成的后 test.s 文件里的汇编代码
--------------------------------------
$ gcc -c test.s -o test.o

选项 -c 让 gcc 在汇编结束后停止编译过程,“test.o” 文件为汇编后生成的机器代码。
  • 链接(Linking)

    • 链接就是将多个目标文件以及所需的库文件链接生成可执行目标文件的过程。
$ gcc test.o -o test
$ ./test
hello world!

--------------------------------------

-o 本质上是一个重命名选项。不使用 -o 选项时,默认生成的是 a.out 文件。这里生成的是可执行文件 test

./test 执行后输出 helloe world!

静态库与动态库原理

静态库

定义:

  • 静态库实际就是一些目标文件(一般以 .o 结尾)的集合,静态库一般以 .a 结尾,只用于生成可执行文件阶段。

  • 在链接步骤中,链接器将从库文件取得所需代码,复制到生成可执行文件中。这种库称为静态库。其特点是可执行文件中包含了库代码的一份完成拷贝,在编译过程中被载入程序中。缺点就是多次使用就会有多分冗余拷贝,并且对程序的更新、部署和发布会带来麻烦,如果静态库有更新,那么所有使用它的程序都需要重新编译和发布。

如何生成静态库

  • 首先生成 test.o 目标文件

  • 使用 ar 命令将 test.o 打包成 libtest.a 静态库

gcc命令如下:

# 首先生成目标文件
$ gcc -c test.c -o test.o

# 使用 ar 命令将目标文件打包成静态库 r:更新或增加新文件到静态库中;c:表示无论存在与否都创建一个库;s:创建文档索引
$ ar rcs libtest.a test.o

# 查看静态库内容
$ ar t libtest.a test.o

动态库

定义:

  • 动态库在链接阶段没有被复制到程序中,而是在程序运行时由系统动态加载到内存中供程序调用。

  • 系统只需载入一次动态库,不同的程序可以得到内存中相同动态库的副本,因此节省了很多内存。

如何生成动态库

  • 首先生成 test.o 目标文件

  • 使用 -shared 和 fPIC 参数生成动态库

gcc命令如下:

# 首先生成目标文件
$ gcc -c test.c -o test.o

# 使用 -shared 和 -fPIC 参数生成动态库 -fPIC:Position Independent Code 表示创建与地址无关的代码供动态库使用。
$ gcc -shared -fPIC -O libtest.so test.o

静态库与动态库区别:

  • 载入时刻不同:

    • 静态库在程序编译时会链接到代码中,程序运行时不再需要静态库,因此体积较大。而且每次编译都需要载入静态代码,因此内存开销大。

    • 动态库在程序编译时不会被链接到目标代码中,而是在程序运行时才被载入,程序运行时需要动态库存在,因此体积较小。而且系统只需要载入一次动态库,不同程序可以得到内存中相同的动态库副本,因此内存开销较小。

makefile 走读与语法基础

什么是 makefile

定义:

  • makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要重新编译,如何进行链接等操作。

  • nakefile 就是“自动化编译”,告诉 make 命令如何编译和链接。

makefile 里有什么

包含以下五个:

  • 显示规则:

    说明如何生成一个或多个目标文件

  • 隐晦规则:

    基于 make 有自动推导过程,所以这个规则可以让我们可以简略的书写makefile

  • 变量定义

    在 makefile 中,可以定义一系列变量

  • 文件指示

    • 在一个 makefile 中引用另一个 makefile,好比 C 语言中的include

    • 根据某些情况,指定 makefile 中的有效部分,就像 C 语言中的预编译

    • 定义多行命令

  • 注释

    makefile 中只有行注释,注释是用 # 字符

makefile 规则

target ... : prerequistics ...
command
或者
target ... : prerequistics ... ; command
  • target:目标文件。可以是 Object File,也可以是执行文件,还可以是标签(Label),甚至是通配符。

  • prerequistics:依赖文件。即要生成那个target所需要的文件或其他 target。

  • command:make 需要执行的命令。反斜杠作为换行符或者分号隔开或者直接换行。

比如下面示例:

# 当前目录存在 main.c、tool.c、tool.h 三个文件
# 下面makefile文件内容
main: main.o tool.o # 要生成一个叫 main 的目标文件
gcc main.o tool.o -o main.o # 生成目标的命令
.PHONY: clean # 显示 clean 是一个“伪目标”
clean:
-rm main *.o # 清除.o生成的文件

----------------------------------------------
// 执行 make 后,输出如下
cc -c -o main.o main.c
cc -c -o tool.o tool.c
gcc main.o tool.o -o main
// 并且声称了一个可执行文件 main
  • -o:指定可执行文件的名称

  • clean:标签,不会生成“clean”文件,这样的 target 称之为“伪目标”,伪目标的名字不能和文件名重复。clean一般放在文件最后

  • .PHONY:显示地指明clean是一个“伪目标”。

makefile 是如何工作的

默认方式下,输入make命令后:

  • make 会在当前目录下找名字叫“Makefile”或“makefile”的文件。

  • 如果找到,它会找文件中第一个目标文件(target),并把这个target作为最终的目标文件,如上面示例中的“main”。

  • 如果main文件不存在,或main所依赖的.o文件的修改时间要比main文件要新,那么它会执行后面所定义的命令来生成main文件。

  • 如果main所依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,若找到则根据规则生成.o文件。

  • make再用.o文件声明make的终极任务,也就是执行文件“main”.

makefile 变量使用

objects = main.o tool.o

main: $(objects)
gcc $(objects) -o main

.PHONY: clean
clean:
-rm main $(objects)

----------------------------------------------
// 执行 make 后,输出如下
cc -c -o main.o main.c
cc -c -o tool.o tool.c
gcc main.o tool.o -o main
  • 为了 makefile 的易维护,在 makefile 中我们可以使用变量。makefile 的变量也就是一个字符串,理解成C语言中的宏即可。

  • 比如:声明一个变量,叫objects,于是,就可以很方便的在makefile中以“$(objects)”的方式来使用这个变量。

引用其他的 Makefile

# 语法格式
include <filename>

# 举例,比如有这样几个Makefile:a.mk、b.mk、c.mk,还有一个文件叫#foo.mk,以及一个变量$(bar),其包含了e.mk和f.mk

Android.mk 基础

Cmake 详解

shell 语法详解

打赏
  • 微信
  • 支付宝

评论